package com.wpam.scrabble.mobile.communication.bt;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import com.wpam.scrabble.mobile.R;
import com.wpam.scrabble.mobile.activities.ServerInfo;
import com.wpam.scrabble.mobile.activities.ServerSearchActivity;
import com.wpam.scrabble.mobile.activities.ServerSearchActivity.ServerAdapter;
import com.wpam.scrabble.mobile.activities.WaitingForAcceptActivity;
import com.wpam.scrabble.mobile.communication.CommunicationManager;
import com.wpam.scrabble.mobile.messages.Message;
import com.wpam.scrabble.mobile.messages.MsgSerializer;

public class BtClientManager {
	
	private DeviceNetworkInfo clientInfo_;
	private Activity parentActivity_;
	private List<AsyncTask<?,?,?>> runningTasks_;
	
	public BtClientManager(Activity parentActivity) {

		this.clientInfo_ = new DeviceNetworkInfo(null, null, null);
		this.parentActivity_ = parentActivity;
		this.runningTasks_ = new ArrayList<AsyncTask<?,?,?>>();
	}
	
	void stopRunningTasks() {
		
		synchronized (runningTasks_) {
			for (AsyncTask<?,?,?> task : runningTasks_)
				task.cancel(true);
		}
	}
	
	public void startReconnectTask() {

		ReconnectTask task = new ReconnectTask();
		task.execute();
	}
	
	public void stopReconnectTask() {
		
		synchronized (runningTasks_) {
			for (AsyncTask<?,?,?> task : runningTasks_) {
				if (task instanceof ReconnectTask) {
					task.cancel(true);
					return;
				}
			}
		}
	}
	
	private void addToRunningTasks(AsyncTask<?,?,?> task) {
		
		synchronized (runningTasks_) {
			runningTasks_.add(task);
		}
	}

	private void removeFromRunningTasks(AsyncTask<?,?,?> task) {
		
		synchronized (runningTasks_) {
			runningTasks_.remove(task);
		}
	}

	public void write(byte[] buffer) throws IOException {		
		clientInfo_.getOutputStream().write(buffer);
	}

	public void disconnectGently() throws IOException {
		clientInfo_.getOutputStream().write(MsgSerializer.serialize(new Message(Message.GOODBYE)));
		BluetoothManager.getInstance().closeConnection(clientInfo_);
	}
	
	public void disconnect() {
		BluetoothManager.getInstance().closeConnection(clientInfo_);
	}
	
	/////////////////////////////////////////////////////////////////////////////////
	//////////////// TASKS
	/////////////////////////////////////////////////////////////////////////////////
	
	class FindServersTask extends AsyncTask<Void, Void, Integer> {
    	
    	private List<BluetoothDevice> foundServers_ = new ArrayList<BluetoothDevice>();
    	private BluetoothSocket sock_ = null;

		@Override
		protected void onPreExecute() {
			
			super.onPreExecute();
			addToRunningTasks(this);
			parentActivity_.findViewById(R.id.serverSearchProgressBar).setVisibility(View.VISIBLE);
			((TextView)parentActivity_.findViewById(R.id.statusText)).setText(parentActivity_.getResources().getString(R.string.search_amongst_paired));
			parentActivity_.findViewById(R.id.refreshButton).setEnabled(false);
		}

		@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
		@Override
		protected Integer doInBackground(Void... params) {
			
	        Set<BluetoothDevice> pairedDevices = BluetoothManager.getInstance().getBondedDevices();
	        BluetoothManager.getInstance().cancelDiscovery();
	        for (BluetoothDevice device : pairedDevices) {
				if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
					try {
						sock_ = device.createRfcommSocketToServiceRecord(BluetoothManager.MY_UUID);
						BluetoothManager.getInstance().cancelDiscovery();
						Timer timer = new Timer();
						timer.schedule(new TimerTask() {
							
							@Override
							public void run() {
								BluetoothManager.getInstance().closeConnection(sock_, null, null);
							}
						}, BluetoothManager.TIMEOUT);
						
						sock_.connect();
						timer.cancel();
						if (isCancelled())
							return null;
						
						foundServers_.add(device);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						//e.printStackTrace();
					} finally {
						BluetoothManager.getInstance().closeConnection(sock_, null, null);
					}
				}
				else {
					device.fetchUuidsWithSdp();
				}
			}
			return foundServers_.size();
		}

		@Override
		protected void onPostExecute(Integer result) {
			
			super.onPostExecute(result);
			if (result > 0) {
				for (BluetoothDevice device : foundServers_) {
					ServerAdapter adapter = ((ServerSearchActivity)parentActivity_).getAdapter();
					adapter.add(new ServerInfo(device, true));
					adapter.notifyDataSetChanged();
				}
				
				AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity_);
				builder.setTitle(parentActivity_.getResources().getString(R.string.discovery_continuation));
				builder.setMessage(parentActivity_.getResources().getString(R.string.do_you_want_to_start_discovery));
				
				builder.setPositiveButton("Tak", new DialogInterface.OnClickListener() {
					
					public void onClick(DialogInterface dialog, int which) {
						BluetoothManager.getInstance().startDiscovery();
						dialog.dismiss();
					}
				});

				builder.setNegativeButton("Nie", new DialogInterface.OnClickListener() {
					
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						parentActivity_.runOnUiThread(new Runnable() {
							
							public void run() {
								((ServerSearchActivity) parentActivity_).uiSetStatus(parentActivity_.getResources().getString(R.string.search_completed), false, true);
							}
						});
					}
				});
				
				if (!parentActivity_.isFinishing())
					builder.create().show();
			}
			else
				BluetoothManager.getInstance().startDiscovery();
			removeFromRunningTasks(this);
		}	
    }

    class ConnectTask extends AsyncTask<BluetoothDevice, Void, Void> {

    	private ProgressDialog dialog_;
    	private boolean error_;
    	private final byte[] buffer_ = MsgSerializer.serialize(new Message(Message.CONNECT));
		
		@Override
		protected void onPreExecute() {
			
			super.onPreExecute();
			addToRunningTasks(this);
			dialog_ = new ProgressDialog(parentActivity_);
			dialog_.setProgressStyle(ProgressDialog.STYLE_SPINNER);
			dialog_.setTitle(parentActivity_.getResources().getString(R.string.connecting));
			dialog_.setMessage(parentActivity_.getResources().getString(R.string.connecting));
			dialog_.show();
		}

		@Override
		protected Void doInBackground(BluetoothDevice... params) {
			if (params[0] != null) {
				CommunicationManager.getInstance(parentActivity_).startClientMsgParser();
				try {
					BluetoothManager.getInstance().cancelDiscovery();
					clientInfo_.setSock_(params[0].createRfcommSocketToServiceRecord(BluetoothManager.MY_UUID));
					Timer timer = new Timer();
					timer.schedule(new TimerTask() {
						
						@Override
						public void run() {
							BluetoothManager.getInstance().closeConnection(clientInfo_);
						}
					}, BluetoothManager.LONG_TIMEOUT);
					
					clientInfo_.getSock().connect();
					timer.cancel();
					if (isCancelled())
						return null;
					
					ReaderTask task = new ReaderTask();
					task.execute();
					
					clientInfo_.getOutputStream().write(buffer_);
				} catch (IOException e) {
					e.printStackTrace();
					error_ = true;
					BluetoothManager.getInstance().closeConnection(clientInfo_);
				}
			}
			Log.i("SCRABBLE_CLIENT", "ConnectTask ended.");
			return null;
		}

		@Override
		protected void onPostExecute(Void result) {
			
			super.onPostExecute(result);
			dialog_.dismiss();
			removeFromRunningTasks(this);
			if (error_)
				((ServerSearchActivity) parentActivity_).uiSetStatus(parentActivity_.getResources().getString(R.string.connecting_failed), false, true);
		}
    }
    
	private class ReconnectTask extends AsyncTask<BluetoothDevice, Void, Void> {
		
		private final byte[] buffer_ = MsgSerializer.serialize(new Message(Message.CONNECT));
		
		@Override
		protected void onPreExecute() {
			
			super.onPreExecute();
			addToRunningTasks(this);
		}
		
		@Override
		protected Void doInBackground(BluetoothDevice... params) {
			
			if (params[0] != null) {
				BluetoothDevice server = (BluetoothDevice) params[0];
				boolean error = true;
				while (!isCancelled() && error) {
					try {
						BluetoothManager.getInstance().cancelDiscovery();
						clientInfo_.setSock_(server.createRfcommSocketToServiceRecord(BluetoothManager.MY_UUID));
						Timer timer = new Timer();
						timer.schedule(new TimerTask() {

							@Override
							public void run() {
								BluetoothManager.getInstance().closeConnection(clientInfo_);
							}
						}, BluetoothManager.LONG_TIMEOUT);

						clientInfo_.getSock().connect();
						timer.cancel();
						if (isCancelled())
							return null;
		 
		 				error = false;
						ReaderTask task = new ReaderTask();
						task.execute();

						clientInfo_.getOutputStream().write(buffer_);
					} catch (IOException e) {
					}
				}
			}
			return null;
		}

		@Override
		protected void onPostExecute(Void result) {

			super.onPostExecute(result);
			removeFromRunningTasks(this);
		}
		
		@Override
		protected void onCancelled() {

			BluetoothManager.getInstance().closeConnection(clientInfo_);
			super.onCancelled();
		}
	}
	
    private class ReaderTask extends AsyncTask<Void, Void, Void> {

    	private byte[] buffer_ = new byte[BluetoothManager.BUFFER_SIZE];
    	
		@Override
		protected void onPreExecute() {
			
			super.onPreExecute();
			addToRunningTasks(this);
		}
		
		@Override
		protected Void doInBackground(Void... params) {
			
			if (clientInfo_.getInputStream() == null) {
				try {
					clientInfo_.setInputStream_(clientInfo_.getSock().getInputStream());
				} catch (IOException e) {
					// TODO Je�li tu jeste�my to mamy spory b��d - nie czytamy nic od serwera...
					e.printStackTrace();
					clientInfo_.setInputStream_(null);
					cancel(true);
				}
			}
			while (!isCancelled()) {
				try {
					clientInfo_.getInputStream().read(buffer_);
					if (isCancelled())
						return null;
					Message msg = MsgSerializer.deserialize(buffer_);
					if (msg == null)
						continue;
					msg.setSenderDevice(clientInfo_.getSock().getRemoteDevice());
					CommunicationManager.getInstance().getMessageBuffer().put(msg);
				} catch (IOException e) {
					e.printStackTrace();
					BluetoothManager.getInstance().closeConnection(clientInfo_);
					return null;
				}
			}
			Log.i("SCRABBLE_CLIENT", "ReaderTask ended.");
			return null;
		}
		
		@Override
		protected void onPostExecute(Void result) {

			super.onPostExecute(result);
			if (!parentActivity_.isFinishing() && parentActivity_ instanceof WaitingForAcceptActivity) {
				Builder builder = new Builder(parentActivity_);
				builder.setTitle(parentActivity_.getResources().getString(R.string.rejection));
				builder.setMessage(parentActivity_.getResources().getString(R.string.connection_rejected_by_server));
				builder.setNeutralButton("OK", new OnClickListener() {
					
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						parentActivity_.finish();
					}
				});
				builder.create().show();
			}
			removeFromRunningTasks(this);
		}

		@Override
		protected void onCancelled() {

			BluetoothManager.getInstance().closeConnection(clientInfo_);
			super.onCancelled();
		}	
    }
}
